/*
 * Copyright (C) 2012 haru Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.harusource.di.mapper.impl;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import android.content.Context;
import android.util.SparseArray;

import com.harusource.di.core.HaruCore;
import com.harusource.di.annotation.Event;
import com.harusource.di.delegate.Delegate;
import com.harusource.di.exception.ViewNotFoundException;
import com.harusource.di.mapper.AnnotaionMapper;

public class EventAnnotationMapper implements AnnotaionMapper {
    private static final int OPTIMIZED_SIZE = 50;
    
    private Map<Class<?>, Class<Delegate>> mDelegates = null;

    synchronized public void setup(Set<Class<?>> classes) {
        // Check if need setup
        if (classes == null || mDelegates != null){
            return;
        }

        try {
            mDelegates = new HashMap<Class<?>, Class<Delegate>>(OPTIMIZED_SIZE);
            for(Class<?> clazz : classes){
                // Add a subclass of Delegate to mDelegates
                if (Delegate.class.isAssignableFrom(clazz)
                        && !Modifier.isAbstract(clazz.getModifiers())) {
                    mDelegates.put(clazz.getInterfaces()[0], (Class<Delegate>)clazz);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Class<?> getAnnotaion() {
        return Event.class;
    }
    
    public Object getCache(){
        return new SparseArray<Map<Class<?>, Delegate>>();
    }

    public <T1, T2> void inject(Context context, Annotation annotation, Object target,
            T1 eventhandle, T2 cache) {
        Class<Delegate> clazz = mDelegates.get(((Event)annotation).clazz());
        Method method = Delegate.findEventHandler(clazz, (Method)eventhandle);
        
        // specified Delegate class is unmatched.
        if(clazz == null || method == null){
            throw new RuntimeException(HaruCore.ERROR_MSG4 + "[" + ((Event)annotation).clazz() + "]");
        }
        
        try{
            for (int id : ((Event) annotation).id()) {
                // create a new delegate instance and make a
                // relationship
                // between the delegate and a view
                Delegate delegate = Delegate.getDelegateInstanceFromCache(clazz, (SparseArray<Map<Class<?>, Delegate>>) cache, id);
                delegate.add(method, target, (Method) eventhandle,
                        HaruCore.findViewById(target, id));
            }
        }catch(ViewNotFoundException e){
            throw new RuntimeException(String.format(HaruCore.ERROR_MSG1, ((Method)eventhandle).getName()));
        }
    }
}